#include "../include/my_redis.h"
#include <stdarg.h>
#include "string.h"


MyRedis::MyRedis()
{
    this->redis_client==nullptr;
}

MyRedis::~MyRedis()
{
    if(this->redis_client){
        redisFree(this->redis_client);
        printf("成功释放一个连接\n");
    }
    
}

bool MyRedis::setExNx(const char *key, const char *value, int expireTime)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"set %s %s ex %d ",key,value,expireTime);
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        return false;
    }
    if(reply->type==REDIS_REPLY_STATUS && strcmp(reply->str,"OK")==0){
        printf("设置成功\n");
        freeReplyObject(reply);
        return true;
    }else{
        printf("%s\n",reply->str);
    }
    freeReplyObject(reply);
    return false;
}

bool MyRedis::connect_redis(const char *ip, int port)
{
    this->redis_client=redisConnect(ip,port);   //可能会返回nullptr
    if(redis_client==nullptr||redis_client->err){
        perror("连接失败");
        if(redis_client){
            printf("连接出错 %s \n",redis_client->errstr);
            redis_client=nullptr;   //置空，后续进行命令执行前，先检查
        }
        return false;

    }

    return true;
}

bool MyRedis::AUTH(const char *auth)
{   

    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"AUTH %s",auth);
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        return false;
    }
    if(reply->type==REDIS_REPLY_ERROR){
        printf("密码错误 %s\n",reply->str);
        return false;
    }
    freeReplyObject(reply);

    return true;
}

bool MyRedis::del_key(const char *key)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"del  %s",key);
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        return false;
    }
    if(reply->integer==0|| reply->type!=REDIS_REPLY_INTEGER){
        printf("删除失败,该key值可能不存在 \n");
        freeReplyObject(reply);
        return false;
    }
    freeReplyObject(reply);
    return true;
}

bool MyRedis::set(const char *key, const char *value)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"set  %s %s",key,value);
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        return false;
    }
    if(reply->type==REDIS_REPLY_ERROR){
        printf("设置失败 %s\n",reply->str);
        freeReplyObject(reply);
        return false;
    }
    freeReplyObject(reply);
    return true;


}

std::string MyRedis::get(const char *key)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"get  %s",key);
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        
        return "";
    }
    if(reply->type==REDIS_REPLY_NIL){
        printf("该key值不存在 :%s\n",reply->str);
        freeReplyObject(reply);
        return "";
    }
    std::string str=reply->str;
    freeReplyObject(reply);
    return str;
    
}

int MyRedis::incrby(const char *key, int num)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"incrby  %s %d",key,num);
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        return -1;
    }
    if(reply->type!=REDIS_REPLY_INTEGER){
        printf("增加失败 %s\n",reply->str);
        freeReplyObject(reply);
        return -1;
    }
    int res=reply->integer;
    freeReplyObject(reply);
    return res;

}

bool MyRedis::mset(const char *key, const char *value, ...)
{
    va_list ap;
    va_start(ap,value); //第一个参数表示存放后续可变参数的东西，第二个参数是最后一个非可变参数，告知编译器从哪里开始取可变参数
    std::string command="mset ";
    
    command+=key;
    command+=" ";
    command+=value;
    while(1){
        const char *kn=va_arg(ap,const char *);    //从哪里提取，提取什么类型
        if(kn==nullptr){
            break;
        }

        const char * vn=va_arg(ap,const char *);
        if(vn==nullptr){
            break;
        }   

        // 这样子设计，还避免了可能的潜在错误，就比如有key没value
        command+=" ";
        command+=kn;
        command+=" ";
        command+=vn;
    }

    //提取完毕
    va_end(ap); //清理资源
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,command.c_str(),command.length());
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        
        return false;
    }
    if(reply->type!=REDIS_REPLY_STATUS){    //不是返回状态那么
        printf("设置失败 %s\n",reply->str);
        freeReplyObject(reply);
        
        return false;
    }
    freeReplyObject(reply);
    
    return true;
    
}

std::list<std::string> MyRedis::mget(const char *key, ...)
{
    va_list ap;
    va_start(ap,key);
    std::string command;
    command+="mget ";
    command+=key;
    while(1){
        const char *kn=va_arg(ap,const char *);
        if(kn==nullptr){
            break;
        }
        command+=" ";
        command+=kn;
    }
    va_end(ap);
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,command.c_str(),command.length());
    std::list<std::string> list;
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        
        return list;
    }
    if(reply->type!=REDIS_REPLY_ARRAY){
        printf("设置失败 %s\n",reply->str);
        freeReplyObject(reply);
       
        return list;
    }
    //遍历获取数据
    for(int i=0;i<reply->elements;++i){
        if(reply->element[i]->type==REDIS_REPLY_NIL){
            list.emplace_back("");
            continue;
        }
        list.emplace_back(reply->element[i]->str);
    }
    
    return  list;
}


bool MyRedis::select(int index)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"select %d",index);
    if(reply==nullptr){
        perror("redis 连接可能出错了");
        return false;
    }
    if(reply->type!=REDIS_REPLY_STATUS){
        printf("选择失败 %s\n",reply->str);
        freeReplyObject(reply);
        return false;
    }
    freeReplyObject(reply);
    return false;
}

int MyRedis::hset(const char *key,const char*field,const char *value){
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"hset %s %s %s",key,field,value);
    int res=-1;
    if(reply==nullptr){
        perror("连接可能有问题");
        return -1;
    }
    if(reply->type==REDIS_REPLY_INTEGER){
        res=reply->integer;
    }

    freeReplyObject(reply);
    return res;

}

int MyRedis::hget(const char *key, const char *field)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"hget %s %s",key,field);
    int res=-1;
    if(reply==nullptr){
        perror("连接可能有问题");
        return res;
    }
    if(reply->type==REDIS_REPLY_STRING){
        res=std::atoi(reply->str);
    }
    freeReplyObject(reply);

    return res;
}

int MyRedis::hincrby(const char *key, const char *field, int num)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"hincrby %s %s %d",key,field,num);
    int res=-1;
    if(reply==nullptr){
        perror("连接可能有问题");
        return res;
    }
    if(reply->type==REDIS_REPLY_INTEGER){
        res=reply->integer;
    }

    freeReplyObject(reply);

    return res;
}

int MyRedis::hdel(const char *key, const char *field)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"hdel %s %s ",key,field);
    int res=-1;
    if(reply==nullptr){
        perror("连接可能有问题");
        return res;
    }
    if(reply->type==REDIS_REPLY_INTEGER){
        res=reply->integer;
    }

    freeReplyObject(reply);

    return res;

    
}


int MyRedis::sadd(const char *key, const char *value)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"sadd %s %s ",key,value);
    int res=-1;
    if(reply==nullptr){
        perror("连接可能有问题");
        return res;
    }
    if(reply->type==REDIS_REPLY_INTEGER){
        res=reply->integer;
    }

    return res;
}

int MyRedis::srem(const char *key, const char *value)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"srem %s %s ",key,value);
    int res=-1;
    if(reply==nullptr){
        perror("连接可能有问题");
        return res;
    } 
    if(reply->type==REDIS_REPLY_INTEGER){
        res=reply->integer;
    }

    return res;
}

int MyRedis::sismember(const char *key, const char *value)
{
    redisReply * reply=(redisReply *)redisCommand(this->redis_client,"sismember %s %s ",key,value);
    int res=-1;
    if(reply==nullptr){
        perror("连接可能有问题");
        return res;
    } 
    if(reply->type==REDIS_REPLY_INTEGER){
        res=reply->integer;
    }

    return res;
}











